package com.zhuiwei.springbootcache.service;

import com.zhuiwei.springbootcache.bean.Employee;
import com.zhuiwei.springbootcache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

/**
 * @author zhuwei
 * @version 2019/05/22
 */
//@CacheConfig(cacheNames = "emp") //抽查缓存共用配置
@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    /**
     * 运行流程：
     * 1.方法运行之前，先去Cache（缓存组件），按照cacheNames指定的名字获取;
     * (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建
     *
     * 2.去Cache中查找缓存的内容，使用一个key，默认就是方法的参数
     *   -key是按照某种策略生产的，默认是使用keyGenerator生成的，默认使用SimpleKeyGenerator生产key；
     *    --SimpleKeyGenerator生产key的默认策略
     *       > 如果没有参数：key=new SimpleKeyGenerator()；
     *       > 如果有一个参数：key=参数的值
     *       > 如果有多个参数：key=new SimpleKeyGenerator（params）
     *
     *
     * 3.没有查到缓存就调用目标方法
     *
     * 4.将目标方法返回的结果，放进缓存中
     *
     * @Cacheable
     * 这个注解标注的方法在执行之前先检查缓存中有没有这个数据，默认按照参数的值作为key去查询缓存
     * 如果没有就运行方法并将结果放入缓存，以后再来调用就可以直接使用缓存中的数据
     *
     *
     * 几个属性：
     * 1.cacheNames/value：指定缓存组件的名字，将方法的返回结果放在哪个缓存中，是数组的方式，可以指定多个缓存。使用@CacheConfig(cacheNames = "emp"),此处可以不写value配置
     *
     * 2.key：缓存的 key可以为空，如果指定要按照 SpEL 表达式编写，如果不指定，默认按照方法的所有参数进行组合。如：该方法传递id=1，key就为"1-方法的返回值"
     *        SpEl方式：#id--参数id的值、 #a0、#p0、#root.args[0]。如：key="#root.methodName+'['+#id+']'"
     *
     * 3.keyGenerator：key的生产器，可以自己指定key的生成器的组件id
     * 说明：key和keyGenerator二选一
     *
     * 4.cacheManager：指定缓存管理器或者cacheResolver指定缓存解析器
     *
     * 5.condition：指定符合的情况下才缓存
     *
     * 6.unless：否定缓存，当unless指定的条件为true，方法的返回值就不会被缓存，可以获取到结果进行判断。如：
     *           unless="#result == null"
     *           unless="#id"
     * 7.sync：是否使用异步模式
     */
//    @Cacheable(value={"emp"},keyGenerator = "myKeyGenerator",condition = "#id>0",unless = "#id==2")
    @Cacheable(value={"emp"})
    public Employee getEmp(Integer id){
        System.out.println("查询员工id："+id);
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

    /**
     * @CachePut
     * 这个注解既调用方法修改数据中的某个数据,后将目标方法的结果缓存起来或更新缓存中已经存在的数据
     *
     * 测试步骤
     * ①查询1号员工，查询到的结果会放在缓存中
     * ②之后的查询都是①缓存中的数据
     * ③更新1号员工，将方法的返回值放进缓存了
     * ④查询1号员工，发现数据还是①中缓存的数据。
     *      --要查询更新后的数据使用如下，这样调用updateEmp后在请求getEmp此时从缓存中取到的值就是最新的
     *          > key="#employee.id"  使用传入的参数的员工id
     *          或
     *          > key="#result.id"  使用返回后的id
     *      --为什么数据还是①中缓存的数据?
     *          > 因为1号员工没有在缓存中更新
     *
     */
    @CachePut(value = "emp",key="#employee.id")
    public Employee updateEmp(Employee emoployee){
        System.out.println("更新员工："+emoployee);
        employeeMapper.updateEmployee(emoployee);
        return emoployee;
    }

    /**
     * @CacheEvict
     * 这个注解标识清除缓存
     *
     * allEntries=true：删除所有缓存
     * beforeInvocation=true：方法之前执行，默认是在方法之后执行；如果执行sql出错，在方法之后执行就不会删除缓存，在方法之前删，无论方法执行是否异常都会上缓存；
     *
     */
    @CacheEvict(value = "emp",key="#id")
    public void deleteEmp(Integer id){
        System.out.println("删除员工，id="+id);
        employeeMapper.deleteEmpById(id);
    }

    /**
     * @Caching 定义复杂缓存规则
     *
     * cacheable：方法可缓存，将方法运行结果缓存
     * put：方法之后调用
     */
    @Caching(
            cacheable={@Cacheable(value="emp",key="#lastName")},
            put={@CachePut(value="emp",key="#result.id" ),
                 @CachePut(value="emp",key="#result.email")
            }
    )
    public Employee getEmpByLastName(String lastName){
        return employeeMapper.getEmpByLastName(lastName);    }
}
